home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 001 / pibmdos.arc / PIBMDOS.PAS next >
Pascal/Delphi Source File  |  1986-09-27  |  37KB  |  700 lines

  1. (*--------------------------------------------------------------------------*)
  2. (*             PIBMDOS.PAS --- Multitasker interface routines               *)
  3. (*--------------------------------------------------------------------------*)
  4. (*                                                                          *)
  5. (*  Author:  Philip R. Burns                                                *)
  6. (*                                                                          *)
  7. (*  Date:    Version 1.0: January, 1986.   DoubleDos support.               *)
  8. (*           Version 2.0: April, 1986.     Add DesqView support.            *)
  9. (*           Version 3.0: July, 1986.      Add TopView/Windows support.     *)
  10. (*           Version 3.1: September, 1986. Update for TaskView support.     *)
  11. (*                                                                          *)
  12. (*  Systems: MS DOS or PC DOS with DoubleDos/DesqView/TopView/Windows       *)
  13. (*           installed.                                                     *)
  14. (*                                                                          *)
  15. (*  History: These routines provide a simple interface for PibTerm          *)
  16. (*           with SoftLogic's DoubleDos multitasking executive,             *)
  17. (*           Quarterdeck's DesqView multitasker, IBM's TopView,             *)
  18. (*           MicroSoft's Windows, and Sunny Hill's TaskView.                *)
  19. (*           (Windows is handled as a Topview-emulating product.  This is   *)
  20. (*           also true for TaskView and DesqView, but those programs do     *)
  21. (*           not require the explicit screen updates TopView requires.      *)
  22. (*                                                                          *)
  23. (*           If you have another multitasker, you should be able to         *)
  24. (*           replace these routines fairly easily with similar-acting       *)
  25. (*           ones for your multitasker.  Use the global types defined       *)
  26. (*           for MultiTasker and MultiTaskerType.                           *)
  27. (*                                                                          *)
  28. (*           With DoubleDos, it is necessary to reobtain the display buffer *)
  29. (*           address every time the screen memory is written to.  With      *)
  30. (*           DesqView, this is unnecessary.  With TopView and Windows,      *)
  31. (*           it is necessary to inform them that the screen has changed.    *)
  32. (*           TaskView works like DesqView.                                  *)
  33. (*                                                                          *)
  34. (*           There are routines for suspending/unsuspending timesharing     *)
  35. (*           included here, but the only actual code provided is for        *)
  36. (*           DoubleDos.  This is because it is rarely necessary to freeze   *)
  37. (*           programs in the TopView-like group, but it IS necessary for    *)
  38. (*           DoubleDos to ensure that, when a large screen update is being  *)
  39. (*           performed, no task switch occurs during the middle of the      *)
  40. (*           update.                                                        *)
  41. (*                                                                          *)
  42. (*--------------------------------------------------------------------------*)
  43. (*                                                                          *)
  44. (*           Please leave messages on Gene Plantz's BBS (312) 882 4145      *)
  45. (*           or Ron Fox's BBS (312) 940 6496.                               *)
  46. (*                                                                          *)
  47. (*--------------------------------------------------------------------------*)
  48.  
  49. (*----------------------------------------------------------------------*)
  50. (*    Color_Screen_Active --- Determine if color or mono screen         *)
  51. (*----------------------------------------------------------------------*)
  52.  
  53. FUNCTION Color_Screen_Active : BOOLEAN;
  54.  
  55. (*----------------------------------------------------------------------*)
  56. (*                                                                      *)
  57. (*     Function:   Color_Screen_Active                                  *)
  58. (*                                                                      *)
  59. (*     Purpose:    Determines if color or mono screen active            *)
  60. (*                                                                      *)
  61. (*     Calling Sequence:                                                *)
  62. (*                                                                      *)
  63. (*        Color_Active := Color_Screen_Active : BOOLEAN;                *)
  64. (*                                                                      *)
  65. (*           Color_Active --- set to TRUE if the color screen is        *)
  66. (*                            active, FALSE if the mono screen is       *)
  67. (*                            active.                                   *)
  68. (*                                                                      *)
  69. (*     Calls:   INTR                                                    *)
  70. (*                                                                      *)
  71. (*----------------------------------------------------------------------*)
  72.  
  73. VAR
  74.    Regs : RegPack;
  75.  
  76. BEGIN  (* Color_Screen_Active *)
  77.  
  78.    Regs.Ax := 15 SHL 8;
  79.  
  80.    INTR( $10 , Regs );
  81.  
  82.    Color_Screen_Active := ( Regs.Al <> 7 );
  83.  
  84. End    (* Color_Screen_Active *);
  85.  
  86. (*----------------------------------------------------------------------*)
  87. (*     Current_Video_Mode --- Determine current video mode setting      *)
  88. (*----------------------------------------------------------------------*)
  89.  
  90. FUNCTION Current_Video_Mode: INTEGER;
  91.  
  92. (*----------------------------------------------------------------------*)
  93. (*                                                                      *)
  94. (*     Function:   Current_Video_Mode                                   *)
  95. (*                                                                      *)
  96. (*     Purpose:    Gets current video mode setting from system          *)
  97. (*                                                                      *)
  98. (*     Calling Sequence:                                                *)
  99. (*                                                                      *)
  100. (*        Current_Mode := Current_Video_Mode : INTEGER;                 *)
  101. (*                                                                      *)
  102. (*           Current_Mode --- set to integer representing current       *)
  103. (*                            video mode inherited from system.         *)
  104. (*                                                                      *)
  105. (*     Calls:   INTR                                                    *)
  106. (*                                                                      *)
  107. (*----------------------------------------------------------------------*)
  108.  
  109. VAR
  110.    Regs : RegPack;
  111.  
  112. BEGIN  (* Current_Video_Mode *)
  113.  
  114.    Regs.Ax := 15 SHL 8;
  115.  
  116.    INTR( $10 , Regs );
  117.  
  118.    Current_Video_Mode := Regs.Al;
  119.  
  120. End    (* Current_Video_Mode *);
  121.  
  122. (*----------------------------------------------------------------------*)
  123. (*        Get_Screen_Address --- Get address of current screen          *)
  124. (*----------------------------------------------------------------------*)
  125.  
  126. PROCEDURE Get_Screen_Address( VAR Actual_Screen : Screen_Ptr );
  127.  
  128. (*----------------------------------------------------------------------*)
  129. (*                                                                      *)
  130. (*     Procedure:  Get_Screen_Address                                   *)
  131. (*                                                                      *)
  132. (*     Purpose:    Gets screen address for current type of display      *)
  133. (*                                                                      *)
  134. (*     Calling Sequence:                                                *)
  135. (*                                                                      *)
  136. (*        Get_Screen_Address( VAR Actual_Screen : Screen_Ptr );         *)
  137. (*                                                                      *)
  138. (*           Actual_Screen --- pointer whose value receives the         *)
  139. (*                             current screen address.                  *)
  140. (*                                                                      *)
  141. (*     Calls:   Color_Screen_Active                                     *)
  142. (*              PTR                                                     *)
  143. (*              TimeSharingActive                                       *)
  144. (*                                                                      *)
  145. (*     Remarks:                                                         *)
  146. (*                                                                      *)
  147. (*        This routine assumes that 'IsTimeSharingActive' has already   *)
  148. (*        been called so that the value of 'Virtual_Screen' is defined. *)
  149. (*                                                                      *)
  150. (*----------------------------------------------------------------------*)
  151.  
  152. VAR
  153.    Regs: RegPack;
  154.  
  155. BEGIN  (* Get_Screen_Address *)
  156.  
  157.    CASE MultiTasker OF
  158.  
  159.       DoubleDos:  BEGIN
  160.                      Regs.Ax := $EC00;
  161.                      MsDos( Regs );
  162.                      Actual_Screen := PTR( Regs.Es, 0 );
  163.                   END;
  164.                                    (* For TopView family, if graphics mode, *)
  165.                                    (* we must return actual screen address, *)
  166.                                    (* not virtual buffer address.  The      *)
  167.                                    (* virtual buffer is only for the        *)
  168.                                    (* text modes.                           *)
  169.       TaskView,
  170.       TopView,
  171.       MSWindows,
  172.       DesqView:   IF ( Current_Video_Mode <> HiRes_GraphMode ) THEN
  173.                      Actual_Screen := Virtual_Screen
  174.                   ELSE
  175.                      Actual_Screen := PTR( Color_Screen_Address , 0 );
  176.  
  177.       ELSE
  178.                   IF Color_Screen_Active THEN
  179.                      Actual_Screen := PTR( Color_Screen_Address , 0 )
  180.                   ELSE
  181.                      Actual_Screen := PTR( Mono_Screen_Address , 0 );
  182.  
  183.    END  (* CASE *);
  184.  
  185. END    (* Get_Screen_Address *);
  186.  
  187. (*--------------------------------------------------------------------------*)
  188. (*           IsTimeSharingActive --- Checks if multitasker is active        *)
  189. (*--------------------------------------------------------------------------*)
  190.  
  191. FUNCTION IsTimeSharingActive : BOOLEAN;
  192.  
  193. (*--------------------------------------------------------------------------*)
  194. (*                                                                          *)
  195. (*    Function: IsTimeSharingActive                                         *)
  196. (*                                                                          *)
  197. (*    Purpose:  Checks if multitasker is active                             *)
  198. (*                                                                          *)
  199. (*    Calling Sequence:                                                     *)
  200. (*                                                                          *)
  201. (*       Ts_On := IsTimeSharingActive : BOOLEAN;                            *)
  202. (*                                                                          *)
  203. (*          Ts_On --- TRUE if multitasker is active.                        *)
  204. (*                                                                          *)
  205. (*    Calls:  MsDos                                                         *)
  206. (*                                                                          *)
  207. (*--------------------------------------------------------------------------*)
  208.  
  209. VAR
  210.    Regs : RegPack;
  211.  
  212. (*--------------------------------------------------------------------------*)
  213.  
  214. FUNCTION Get_TopView_Screen_Address : BOOLEAN;
  215.  
  216. VAR
  217.    SegS : INTEGER;
  218.    SegO : INTEGER;
  219.  
  220. BEGIN (* Get_TopView_Screen_Address *)
  221.  
  222.    Regs.Di := 0;
  223.    Regs.Ax := $FE00;
  224.  
  225.    IF Color_Screen_Active THEN
  226.       Regs.Es := Color_Screen_Address
  227.    ELSE
  228.       Regs.Es := Mono_Screen_Address;
  229.  
  230.    SegO    := 0;
  231.    SegS    := Regs.Es;
  232.  
  233.    INTR( $10 , Regs );
  234.  
  235.    Virtual_Screen := PTR( Regs.Es , Regs.Di );
  236.  
  237.    Get_TopView_Screen_Address := ( ( Regs.Es <> SegS ) OR ( Regs.Di <> SegO ) );
  238.  
  239. END   (* Get_TopView_Screen_Address *);
  240.  
  241. (*--------------------------------------------------------------------------*)
  242.  
  243. BEGIN (* IsTimeSharingActive *)
  244.                                    (* Assume timesharing not active *)
  245.    IsTimeSharingActive := FALSE;
  246.    MultiTasker         := MultiTasker_None;
  247.  
  248.                                    (* Get initial screen address    *)
  249.    IF Color_Screen_Active THEN
  250.       Virtual_Screen := PTR( Color_Screen_Address , 0 )
  251.    ELSE
  252.       Virtual_Screen := PTR( Mono_Screen_Address  , 0 );
  253.  
  254.                                    (* If DDos is active, $E4 should *)
  255.                                    (* return a non-zero value in Al *)
  256.    Regs.Ax := $E400;
  257.    MsDos( Regs );
  258.  
  259.    IF ( Regs.Al <> 0 ) THEN
  260.       BEGIN
  261.          IsTimeSharingActive := TRUE;
  262.          MultiTasker         := DoubleDos;
  263.          EXIT;
  264.       END;
  265.                                    (* See if DesqView is active.     *)
  266.                                    (* We do a time/date call with    *)
  267.                                    (* DESQ as date.  If DesqView is  *)
  268.                                    (* active, this will be accepted. *)
  269.                                    (* If not, it returns as invalid. *)
  270.                                    (* While we're at it, get the     *)
  271.                                    (* display buffer address, which  *)
  272.                                    (* never changes.                 *)
  273.    Regs.Ax := $2B01;
  274.    Regs.Cx := $4445;  (*'DE'*)
  275.    Regs.Dx := $5351;  (*'SQ'*)
  276.    MsDos( Regs );
  277.  
  278.    IF ( Regs.Al <> $FF ) THEN
  279.       IF Get_TopView_Screen_Address THEN
  280.          BEGIN
  281.             IsTimeSharingActive := TRUE;
  282.             MultiTasker         := DesqView;
  283.             EXIT;
  284.          END;
  285.                                    (* Check for TaskView or TopView.  We do   *)
  286.                                    (* a request for a TopView version number. *)
  287.                                    (* If BX comes back $0001, this must be    *)
  288.                                    (* TaskView.  Anything non-zero indicates  *)
  289.                                    (* TopView or a compatible program.        *)
  290.    Regs.Ax := $1022;
  291.    Regs.Bx := 0;
  292.    INTR( $15 , Regs );
  293.  
  294.    IF ( Regs.Bx <> 0 ) THEN
  295.       BEGIN
  296.  
  297.          IF ( Regs.Bx = 1 ) THEN
  298.             MultiTasker         := TaskView
  299.          ELSE
  300.             MultiTasker         := TopView;
  301.  
  302.          IF ( NOT Get_TopView_Screen_Address ) THEN
  303.             MultiTasker := Multitasker_None
  304.          ELSE
  305.             IsTimeSharingActive := TRUE;
  306.  
  307.       END;
  308.  
  309. END   (* IsTimeSharingActive *);
  310.  
  311. (*--------------------------------------------------------------------------*)
  312. (*    TurnOnTimeSharing --- allow timesharing to proceed                    *)
  313. (*--------------------------------------------------------------------------*)
  314.  
  315. PROCEDURE TurnOnTimeSharing;
  316.  
  317. (*--------------------------------------------------------------------------*)
  318. (*                                                                          *)
  319. (*    Procedure:  TurnOnTimeSharing;                                        *)
  320. (*                                                                          *)
  321. (*    Purpose:    Activates timesharing                                     *)
  322. (*                                                                          *)
  323. (*    Calling Sequence:                                                     *)
  324. (*                                                                          *)
  325. (*       TurnOnTimeSharing;                                                 *)
  326. (*                                                                          *)
  327. (*    Calls:  MsDos                                                         *)
  328. (*                                                                          *)
  329. (*--------------------------------------------------------------------------*)
  330.  
  331. VAR
  332.    Regs : RegPack;
  333.  
  334. BEGIN (* TurnOnTimeSharing *)
  335.  
  336.    CASE MultiTasker OF
  337.                                    (* If DDos is active, $EB turns  *)
  338.                                    (* on timesharing                *)
  339.       DoubleDos:   BEGIN
  340.                       Regs.Ax := $EB00;
  341.                       MsDos( Regs );
  342.                    END;
  343.  
  344.       ELSE;
  345.  
  346.    END (* CASE *);
  347.  
  348. END   (* TurnOnTimeSharing *);
  349.  
  350. (*--------------------------------------------------------------------------*)
  351. (*        TurnOffTimeSharing --- suspend timesharing under DoubleDos        *)
  352. (*--------------------------------------------------------------------------*)
  353.  
  354. PROCEDURE TurnOffTimeSharing;
  355.  
  356. (*--------------------------------------------------------------------------*)
  357. (*                                                                          *)
  358. (*    Procedure:  TurnOffTimeSharing;                                       *)
  359. (*                                                                          *)
  360. (*    Purpose:    Suspends timesharing                                      *)
  361. (*                                                                          *)
  362. (*    Calling Sequence:                                                     *)
  363. (*                                                                          *)
  364. (*       TurnOffTimeSharing;                                                *)
  365. (*                                                                          *)
  366. (*    Calls:  MsDos                                                         *)
  367. (*                                                                          *)
  368. (*--------------------------------------------------------------------------*)
  369.  
  370. VAR
  371.    Regs : RegPack;
  372.  
  373. BEGIN (* TurnOffTimeSharing *)
  374.  
  375.    CASE MultiTasker OF
  376.                                    (* If DDos is active, $EA suspends *)
  377.                                    (* timesharing                     *)
  378.       DoubleDos:   BEGIN
  379.                       Regs.Ax := $EA00;
  380.                       MsDos( Regs );
  381.                    END;
  382.  
  383.       ELSE;
  384.  
  385.    END (* CASE *);
  386.  
  387. END   (* TurnOffTimeSharing *);
  388.  
  389. (*--------------------------------------------------------------------------*)
  390. (*            GiveAwayTime --- gives away time slices to other task         *)
  391. (*--------------------------------------------------------------------------*)
  392.  
  393. PROCEDURE GiveAwayTime( NSlices : INTEGER );
  394.  
  395. (*--------------------------------------------------------------------------*)
  396. (*                                                                          *)
  397. (*    Procedure:  GiveAwayTime;                                             *)
  398. (*                                                                          *)
  399. (*    Purpose:    Gives away time slices to other tasks                     *)
  400. (*                                                                          *)
  401. (*    Calling Sequence:                                                     *)
  402. (*                                                                          *)
  403. (*       GiveAwayTime( NSlices :  INTEGER );                                *)
  404. (*                                                                          *)
  405. (*          NSlices --- # of slices (55 ms) to give away, if DoubleDos.     *)
  406. (*                      For other multitaskers, the entire remaining        *)
  407. (*                      time-slice is given up.                             *)
  408. (*                                                                          *)
  409. (*    Calls:  MsDos                                                         *)
  410. (*                                                                          *)
  411. (*--------------------------------------------------------------------------*)
  412.  
  413. VAR
  414.    Regs : RegPack;
  415.  
  416. BEGIN (* GiveAwayTime *)
  417.  
  418.    CASE MultiTasker OF
  419.                                    (* Function EE gives time to other part. *)
  420.       DoubleDos:   BEGIN
  421.                       Regs.Ah := $EE;
  422.                       Regs.Al := NSlices;
  423.                       MsDos( Regs );
  424.                    END;
  425.                                    (* Int 15H for TopView family products *)
  426.       DesqView,
  427.       TopView,
  428.       MSWindows,
  429.       TaskView:    BEGIN
  430.                       Regs.Ax := $1000;
  431.                       INTR( $15 , Regs );
  432.                    END;
  433.  
  434.       ELSE;
  435.  
  436.    END  (* CASE *);
  437.  
  438. END   (* GiveAwayTime *);
  439.  
  440. (*--------------------------------------------------------------------------*)
  441. (*    Sync_Screen --- Synchronizes multitasker screen with hardware screen  *)
  442. (*--------------------------------------------------------------------------*)
  443.  
  444. PROCEDURE Sync_Screen( S_Pos: INTEGER; NChars : INTEGER );
  445.  
  446. (*--------------------------------------------------------------------------*)
  447. (*                                                                          *)
  448. (*    Procedure:  Sync_Screen;                                              *)
  449. (*                                                                          *)
  450. (*    Purpose:    Synchronizes multitasker and hardware screens             *)
  451. (*                                                                          *)
  452. (*    Calling Sequence:                                                     *)
  453. (*                                                                          *)
  454. (*       Sync_Screen( S_Pos : INTEGER; NChars: INTEGER );                   *)
  455. (*                                                                          *)
  456. (*    Calls:  INTR                                                          *)
  457. (*                                                                          *)
  458. (*    Remarks:                                                              *)
  459. (*                                                                          *)
  460. (*       This facility is required by the TopView-family products.          *)
  461. (*                                                                          *)
  462. (*--------------------------------------------------------------------------*)
  463.  
  464. VAR
  465.    Regs  : RegPack;
  466.    Daddr : Screen_Ptr;
  467.  
  468. BEGIN (* Sync_Screen *)
  469.  
  470.    IF ( MultiTasker IN [TopView,MSWindows] ) THEN
  471.       WITH Regs DO
  472.          BEGIN
  473.  
  474.             Regs.Es := SEG( Virtual_Screen^ );
  475.             Regs.Di := OFS( Virtual_Screen^ ) + S_Pos - 1;
  476.             Regs.Cx := NChars SHL 1;
  477.             Regs.Ah := $FF;
  478.  
  479.             INTR( $10 , Regs );
  480.  
  481.          END;
  482.  
  483. END   (* Sync_Screen *);
  484.  
  485. (*--------------------------------------------------------------------------*)
  486. (* Sync_Entire_Screen --- Synchronizes multitasker screen with hardware     *)
  487. (*--------------------------------------------------------------------------*)
  488.  
  489. PROCEDURE Sync_Entire_Screen;
  490.  
  491. (*--------------------------------------------------------------------------*)
  492. (*                                                                          *)
  493. (*    Procedure:  Sync_Entire_Screen;                                       *)
  494. (*                                                                          *)
  495. (*    Purpose:    Synchronizes multitasker and hardware screens             *)
  496. (*                                                                          *)
  497. (*    Calling Sequence:                                                     *)
  498. (*                                                                          *)
  499. (*       Sync_Entire_Screen;                                                *)
  500. (*                                                                          *)
  501. (*    Calls:  INTR                                                          *)
  502. (*                                                                          *)
  503. (*    Remarks:                                                              *)
  504. (*                                                                          *)
  505. (*       This facility is used by the TopView-family products when the      *)
  506. (*       entire screen has been updated.                                    *)
  507. (*                                                                          *)
  508. (*--------------------------------------------------------------------------*)
  509.  
  510. VAR
  511.    Regs : RegPack;
  512.  
  513. BEGIN (* Sync_Entire_Screen *)
  514.  
  515.    IF ( MultiTasker IN [TopView,MSWindows] ) THEN
  516.       WITH Regs DO
  517.          BEGIN
  518.  
  519.             Regs.Es := SEG( Virtual_Screen^ );
  520.             Regs.Di := OFS( Virtual_Screen^ );
  521.             Regs.Cx := Screen_Length SHR 1;
  522.             Regs.Ah := $FF;
  523.  
  524.             INTR( $10 , Regs );
  525.  
  526.          END;
  527.  
  528. END   (* Sync_Entire_Screen *);
  529.  
  530.  
  531. (*----------------------------------------------------------------------*)
  532. (*          WriteSXY --- Write text string to specified row/column      *)
  533. (*----------------------------------------------------------------------*)
  534.  
  535. PROCEDURE WriteSXY( S: AnyStr; X: INTEGER; Y: INTEGER; Color: INTEGER );
  536.  
  537. (*----------------------------------------------------------------------*)
  538. (*                                                                      *)
  539. (*     Procedure:  WriteSXY                                             *)
  540. (*                                                                      *)
  541. (*     Purpose:    Writes text string at specified row and column       *)
  542. (*                 position on screen.                                  *)
  543. (*                                                                      *)
  544. (*     Calling Sequence:                                                *)
  545. (*                                                                      *)
  546. (*        WriteSXY( S: AnyStr; X: INTEGER; Y: INTEGER; Color: INTEGER );*)
  547. (*                                                                      *)
  548. (*           S      --- String to be written                            *)
  549. (*           X      --- Column position to write string                 *)
  550. (*           Y      --- Column position to write string                 *)
  551. (*           Color  --- Color in which to write string                  *)
  552. (*                                                                      *)
  553. (*     Calls:   None                                                    *)
  554. (*                                                                      *)
  555. (*     Remarks:  This routine is based in part on one written by        *)
  556. (*               Dennis Brain.                                          *)
  557. (*                                                                      *)
  558. (*----------------------------------------------------------------------*)
  559.  
  560. BEGIN (* WriteSXY *)
  561.                                    (* Freeze screen for DoubleDos *)
  562.  
  563.    IF ( MultiTasker = DoubleDos ) THEN
  564.       BEGIN
  565.          TurnOffTimeSharing;
  566.          Get_Screen_Address( Virtual_Screen );
  567.       END;
  568.  
  569.    INLINE(
  570.   $1E                    {         PUSH  DS                     ;Save DS}
  571.                          {;}
  572.                          {;  Check if we're going to use BIOS}
  573.                          {;}
  574.   /$A0/>Write_Screen_Memory  {     MOV   AL,[<Write_Screen_Memory]  ;See if we're writing to screen memory}
  575.   /$D0/$D8               {         RCR   AL,1                   ;}
  576.   /$73/$5B               {         JNC   BIOS                   ;No -- skip to BIOS code}
  577.                          {;}
  578.                          {;  Set up for direct screen write.}
  579.                          {;  Get row position and column positions, and offset in screen buffer.}
  580.                          {;}
  581.   /$8B/$46/<Y            {         MOV   AX,[BP+<Y]             ;AX = Row}
  582.   /$48                   {         DEC   AX                     ;Row to 0..24 range}
  583.   /$B9/$04/$00           {         MOV   CX,$0004               ;CL = 4; CH = 0}
  584.   /$D3/$E0               {         SHL   AX,CL                  ;AX = Row * 16}
  585.   /$89/$C3               {         MOV   BX,AX                  ;Sore in BX}
  586.   /$D1/$E0               {         SHL   AX,1                   ;AX = Row * 32}
  587.   /$D1/$E0               {         SHL   AX,1                   ;AX = Row * 64}
  588.   /$01/$D8               {         ADD   AX,BX                  ;AX = (Row * 64) + (Row * 16)}
  589.                          {                                      ;   = Row * 80}
  590.   /$8B/$5E/<X            {         MOV   BX,[BP+<X]             ;BX = Column}
  591.   /$4B                   {         DEC   BX                     ;Col to 0..79 range}
  592.   /$01/$D8               {         ADD   AX,BX                  ;AX = (Row * 80) + Col}
  593.   /$D1/$E0               {         SHL   AX,1                   ;Account for attribute bytes}
  594.   /$89/$C7               {         MOV   DI,AX                  ;Move result into DI}
  595.   /$8D/$76/<S            {         LEA   SI,[BP+<S]             ;DS:SI will point to S[0]}
  596.   /$8B/$16/>Virtual_Screen+2 {     MOV   DX,[>Virtual_Screen+2];DX = Base address of screen}
  597.   /$8E/$C2               {         MOV   ES,DX                  ;ES:DI points to Base:Row,Col}
  598.   /$A0/>Wait_For_Retrace {         MOV   AL,[<Wait_For_Retrace] ;Grab this before changing DS}
  599.   /$8C/$D2               {         MOV   DX,SS                  ;Move SS...}
  600.   /$8E/$DA               {         MOV   DS,DX                  ; into DS}
  601.   /$8A/$0C               {         MOV   CL,[SI]                ;CL = Length(S)}
  602.   /$E3/$72               {         JCXZ  Exit                   ;If string empty, Exit}
  603.   /$46                   {         INC   SI                     ;DS:SI points to S[1]}
  604.   /$8A/$66/<Color        {         MOV   AH,[BP+<Color]         ;AH = Attribute}
  605.   /$FC                   {         CLD                          ;Set direction to forward}
  606.   /$D0/$D8               {         RCR   AL,1                   ;If Snow is False...}
  607.   /$73/$1C               {         JNC   Mono                   ; use "Mono" routine}
  608.                          {;}
  609.                          {;  Color routine (used only when Wait_For_Retrace is True) **}
  610.                          {;}
  611.   /$BA/>CRT_Status       {         MOV   DX,>CRT_Status         ;Point DX to CGA status port}
  612.   /$AC                   {GetNext: LODSB                        ;Load next character into AL}
  613.                          {                                      ; AH already has Attr}
  614.   /$89/$C3               {         MOV   BX,AX                  ;Store video word in BX}
  615.   /$B4/$09               {         MOV   AH,$09                 ;Move horizontal & vertical}
  616.                          {                                      ; retrace mask into AH}
  617.   /$FA                   {         CLI                          ;No interrupts now}
  618.   /$EC                   {WaitH:   IN    AL,DX                  ;Get 6845 status}
  619.   /$D0/$D8               {         RCR   AL,1                   ;Wait for horizontal}
  620.   /$72/$FB               {         JC    WaitH                  ; retrace}
  621.   /$EC                   {WaitV:   IN    AL,DX                  ;Get 6845 status again}
  622.   /$20/$E0               {         AND   AL,AH                  ;Wait for vertical}
  623.   /$74/$FB               {         JZ    WaitV                  ; retrace}
  624.   /$89/$D8               {         MOV   AX,BX                  ;Move word back to AX...}
  625.   /$AB                   {         STOSW                        ; and then to screen}
  626.   /$FB                   {         STI                          ;Allow interrupts}
  627.   /$E2/$EA               {         LOOP  GetNext                ;Get next character}
  628.   /$E9/$4D/$00           {         JMP   Exit                   ;Done}
  629.                          {;}
  630.                          {;  Mono routine (used whenever Wait_For_Retrace is False) **}
  631.                          {;}
  632.   /$AC                   {Mono:    LODSB                        ;Load next character into AL}
  633.                          {                                      ; AH already has Attr}
  634.   /$AB                   {         STOSW                        ;Move video word into place}
  635.   /$E2/$FC               {         LOOP  Mono                   ;Get next character}
  636.   /$E9/$46/$00           {         JMP   Exit                   ;Done}
  637.                          {;}
  638.                          {;  Use BIOS to display string (if Wrie_To_Screen is False) **}
  639.                          {;}
  640.   /$8A/$76/<Y            {Bios:    MOV   DH,[BP+<Y]             ;Get starting row}
  641.   /$FE/$CE               {         DEC   DH                     ;Drop by one for BIOS}
  642.   /$8A/$56/<X            {         MOV   DL,[BP+<X]             ;Get starting column}
  643.   /$FE/$CA               {         DEC   DL                     ;Drop for indexing}
  644.   /$FE/$CA               {         DEC   DL                     ;}
  645.   /$8D/$76/<S            {         LEA   SI,[BP+<S]             ;DS:SI will point to S[0]}
  646.   /$8C/$D0               {         MOV   AX,SS                  ;Move SS...}
  647.   /$8E/$D8               {         MOV   DS,AX                  ; into DS}
  648.   /$8A/$0C               {         MOV   CL,[SI]                ;CL = Length(S)}
  649.   /$E3/$2F               {         JCXZ  Exit                   ;If string empty, Exit}
  650.   /$46                   {         INC   SI                     ;DS:SI points to S[1]}
  651.   /$52                   {         PUSH  DX                     ;Save X and Y}
  652.   /$1E                   {         PUSH  DS                     ;Save DS:SI}
  653.   /$56                   {         PUSH  SI                     ;}
  654.   /$FC                   {         CLD                          ;Forward direction}
  655.                          {;}
  656.   /$51                   {Bios1:   PUSH  CX                     ;Push length}
  657.   /$B4/$02               {         MOV   AH,2                   ;BIOS Position cursor}
  658.   /$B7/$00               {         MOV   BH,0                   ;Page zero}
  659.   /$59                   {         POP   CX}
  660.   /$5E                   {         POP   SI                     ;Get S address}
  661.   /$1F                   {         POP   DS                     ;}
  662.   /$5A                   {         POP   DX                     ;X and Y}
  663.   /$FE/$C2               {         INC   DL                     ;X + 1}
  664.   /$52                   {         PUSH  DX                     ;Save X and Y}
  665.   /$1E                   {         PUSH  DS}
  666.   /$56                   {         PUSH  SI}
  667.   /$51                   {         PUSH  CX}
  668.   /$CD/$10               {         INT   $10                    ;Call BIOS}
  669.   /$B4/$09               {         MOV   AH,9                   ;BIOS Display character}
  670.   /$59                   {         POP   CX}
  671.   /$5E                   {         POP   SI                     ;Get S address}
  672.   /$1F                   {         POP   DS                     ;}
  673.   /$AC                   {         LODSB                        ;Next character into AL}
  674.   /$1E                   {         PUSH  DS                     ;Save S address}
  675.   /$56                   {         PUSH  SI                     ;}
  676.   /$51                   {         PUSH  CX                     ;Length left to do}
  677.   /$B7/$00               {         MOV   BH,0                   ;Display page zero}
  678.   /$8A/$5E/<Color        {         MOV   BL,[BP+<Color]         ;AH = Attribute}
  679.   /$B9/$01/$00           {         MOV   CX,1                   ;One character}
  680.   /$CD/$10               {         INT   $10                    ;Call BIOS}
  681.   /$59                   {         POP   CX                     ;Get back length}
  682.   /$E2/$D9               {         LOOP  Bios1}
  683.                          {;                                     ;Remove stuff left on stack}
  684.   /$5E                   {         POP   SI}
  685.   /$1F                   {         POP   DS}
  686.   /$5A                   {         POP   DX}
  687.                          {;}
  688.   /$1F                   {Exit:    POP   DS                     ;Restore DS}
  689. );
  690.                                    (* Unfreeze screen in DoubleDos *)
  691.  
  692.    IF ( MultiTasker = DoubleDos ) THEN
  693.       TurnOnTimeSharing
  694.                                    (* Synchronize screen for TopView *)
  695.  
  696.    ELSE IF ( MultiTasker = TopView ) THEN
  697.       Sync_Screen( ( ( Y - 1 ) * 80 + X ) SHL 1 - 1 , ORD( S[0] ) );
  698.  
  699. END   (* WriteSXY *);
  700.